﻿namespace PluginParameterGenerator
{
  using System.IO;
  using System.Linq;
  using System.Text;
  using System.Xml;
  using FileHelpers;
  using System.Collections.Generic;
  using System;
  using System.Reflection;

  class Program
  {
    static void Main(string[] args)
    {
      bool validParamas = true;
      string argNamespace = "Xrm.Core.Helpers";
      string argLanguage = "cs";
      foreach (string[] argument in args.Select(a => a.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries)))
      {
        if (argument.Length == 2)
        {
          switch (argument[0].ToLowerInvariant())
          {
            case "/language":
            case "/l":
              switch (argument[1].ToLowerInvariant())
              {
                case "cs":
                case "vb":
                  argLanguage = argument[1].ToLowerInvariant();
                  break;
                default:
                  validParamas = false;
                  break;
              }
              break;
            case "/namespace":
            case "/n":
              argNamespace = argument[1];
              break;
            default:
              validParamas = false;
              break;
          }
        }
        else
        {
          validParamas = false;
        }
      }

      if (validParamas)
      {
        FileHelperEngine engine = new FileHelperEngine(typeof(Parameter));
        Parameter[] inputParameters = engine.ReadFile("Parameters\\InputParameters.csv") as Parameter[];
        Parameter[] outputParameters = engine.ReadFile("Parameters\\OutputParameters.csv") as Parameter[];

        List<Message> messages = new List<Message>();

        foreach (Parameter parameter in inputParameters)
        {
          Message message = messages.FirstOrDefault(m => m.messageName == parameter.messageName);
          if (message == null)
          {
            message = new Message(parameter.messageName);
            messages.Add(message);
          }
          if (message.inputParameters.Count(p => p.parameterName == parameter.parameterName) == 0)
          {
            message.inputParameters.Add(parameter);
          }
        }
        foreach (Parameter parameter in outputParameters)
        {
          Message message = messages.FirstOrDefault(m => m.messageName == parameter.messageName);
          if (message == null)
          {
            message = new Message(parameter.messageName);
            messages.Add(message);
          }
          if (message.outputParameters.Count(p => p.parameterName == parameter.parameterName) == 0)
          {
            message.outputParameters.Add(parameter);
          }
        }

        string messageTemplate;
        using (StreamReader streamReader = new StreamReader(string.Format("Templates\\{0}\\Message.txt", argLanguage)))
        {
          messageTemplate = streamReader.ReadToEnd();
        }

        string inputParameterTemplate;
        using (StreamReader streamReader = new StreamReader(string.Format("Templates\\{0}\\InputParameter.txt", argLanguage)))
        {
          inputParameterTemplate = streamReader.ReadToEnd();
        }

        string outputParameterTemplate;
        using (StreamReader streamReader = new StreamReader(string.Format("Templates\\{0}\\OutputParameter.txt", argLanguage)))
        {
          outputParameterTemplate = streamReader.ReadToEnd();
        }

        StringBuilder messagesBuilder = new StringBuilder();
        foreach (Message message in messages)
        {
          StringBuilder inputParametersBuilder = new StringBuilder();
          StringBuilder outputParametersBuilder = new StringBuilder();

          Dictionary<string, string> typeReplacements = new Dictionary<string, string>();

          typeReplacements.Add("System.Nullable`1[[System.Boolean]]", "bool?");
          typeReplacements.Add("System.Nullable`1[[System.Guid]]", "System.Guid?");
          typeReplacements.Add("System.Nullable`1[[System.Int32]]", "int?");

          foreach (Parameter parameter in message.inputParameters)
          {
            string parameterType = parameter.parameterType.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
            foreach (KeyValuePair<string, string> replacement in typeReplacements)
            {
              parameterType = parameterType.Replace(replacement.Key, replacement.Value);
            }
            inputParametersBuilder.AppendLine(inputParameterTemplate.Replace("[Name]", parameter.parameterName).Replace("[Type]", parameterType));
          }
          foreach (Parameter parameter in message.outputParameters)
          {
            string parameterType = parameter.parameterType.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
            foreach (KeyValuePair<string, string> replacement in typeReplacements)
            {
              parameterType = parameterType.Replace(replacement.Key, replacement.Value);
            }
            outputParametersBuilder.AppendLine(outputParameterTemplate.Replace("[Name]", parameter.parameterName).Replace("[Type]", parameterType));
          }
          messagesBuilder.AppendLine(messageTemplate.Replace("[Namespace]", argNamespace).Replace("[Message]", message.messageName).Replace("[InputParameters]", inputParametersBuilder.ToString()).Replace("[OutputParameters]", outputParametersBuilder.ToString()));
        }

        string pluginHelperTemplate;
        using (StreamReader streamReader = new StreamReader(string.Format("Templates\\{0}\\PluginHelper.txt", argLanguage)))
        {
          pluginHelperTemplate = streamReader.ReadToEnd();
        }

        StringBuilder pluginHelperBuilder = new StringBuilder();
        pluginHelperBuilder.AppendLine(pluginHelperTemplate.Replace("[Messages]", messagesBuilder.ToString()).Replace("[Version]", Assembly.GetExecutingAssembly().GetName().Version.ToString()));

        using (StreamWriter streamWriter = new StreamWriter(string.Format("PluginHelper.{0}", argLanguage)))
        {
          streamWriter.Write(pluginHelperBuilder.ToString());
          streamWriter.Flush();
          streamWriter.Close();
        }

        Console.WriteLine(string.Format("PluginHelper.{0} generated. Press any key to continue.", argLanguage));
        Console.ReadKey();
      }
      else
      {
        Console.WriteLine(string.Format("PPGen : CRM 2011 Plugin Parameter Helper Generator [Version {0}]", Assembly.GetExecutingAssembly().GetName().Version.ToString()));
        Console.WriteLine();

        Console.WriteLine("Options:");

        Console.WriteLine("/language:<language>");
        Console.WriteLine("The language to use for the generated helper code. This can be either 'CS'");
        Console.WriteLine("or 'VB'.  The default language is 'CS'. Short form is '/l:'.");
        Console.WriteLine();

        Console.WriteLine("/namespace:<namespace>");
        Console.WriteLine("The namespace for the generated helper code. The default namespace is");
        Console.WriteLine("Xrm.Core.Helpers. Short form is '/n:'.");
        Console.WriteLine();

        Console.WriteLine("Example:");
        Console.WriteLine("ppgen.exe");
        Console.WriteLine("/language:cs");
        Console.WriteLine("/namespace:Xrm.Core.Helpers");
      }
    }

    private class Message
    {
      public string messageName;
      public List<Parameter> inputParameters = new List<Parameter>();
      public List<Parameter> outputParameters = new List<Parameter>();

      public Message(string name)
      {
        messageName = name;
      }
    }

    [DelimitedRecord(",")]
    private class Parameter
    {
      public string messageName;

      public string parameterName;

      [FieldQuoted('"', QuoteMode.OptionalForBoth)]
      public string parameterType;

      public int primaryObjectTypeCode;
    }
  }
}
